#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "status_tree.h"

inline sST *ST_create(char *name, sFS *fl) {
    sST *tmp=(sST *)calloc(1, sizeof(sST));
    tmp->name=name;
    tmp->files=fl;
    return tmp;
}

sST *ST_append(sST **tree, char *name, sFS *fl) {
    if(!tree) return NULL;

    sST *tmp;

    if(*tree) {
        tmp=*tree;
        while(tmp->next) tmp=tmp->next;
        tmp->next=ST_create(name, fl);
        tmp->next->prev=tmp;
        tmp->next->parent=tmp->parent;
        tmp=tmp->next;
    }
    else {
        tmp=*tree=ST_create(name, fl);
    }

    return tmp;
}

inline sST *ST_append_child(sST *tree, char *name, sFS *fl) {
    if(!tree) return NULL;

    sST *tmp=ST_append(&tree->child, name, fl);
    tmp->parent=tree;

    return tmp;
}

inline void *memdup(void *src, unsigned int size) {
    return memcpy(malloc(size), src, size);
}

sST *ST_dup(sST *src_root, unsigned int nd) {   // omg, what a bad code to avoid the recursive form...
    if(!src_root) return NULL;

    sST *src=src_root, *dst_root=NULL, **pdst=&dst_root, *parent=NULL, *prev=NULL, *new=NULL;

    // 'pdst' is the pointer to the location where i have to duplicate the 'src' tree element

    while(1) {
        // let's duplicate the current 'src' element
        new=(*pdst)=ST_create(strdup(src->name), (sFS *)memdup((void *)src->files, nd*sizeof(sFS)));
        new->prev=prev;
        new->parent=parent;

        if(src->child) {    // the current 'src' element got a child, let's add it in the dst tree (with the right parent and prev) next loop
            prev=NULL;
            parent=*pdst;
            pdst=&((*pdst)->child);
            src=src->child;
            continue;
        }

        if(src->next) {     // the current 'src' element got a next element, let's add it in the dst tree (with the right prev) next loop
            prev=(*pdst);
            pdst=&((*pdst)->next);
            src=src->next;
            continue;
        }

        do {    // the current 'src' element got a parent element, i go up the tree while i haven't a next element
            if(!src->parent) return dst_root;

            pdst=&((*pdst)->parent);
            src=src->parent;
        } while(!src->next);

        prev=(*pdst);
        parent=prev->parent;
        pdst=&((*pdst)->next);
        src=src->next;
    }
}

void _ST_free_data_cb(sST *el) {
    if(el->name) free(el->name);
    if(el->files) free(el->files);
    free(el);
}

void ST_remove_cb(sST **tree, sST *el, ST_HANDLER cb) {
    if(!tree || !*tree || !el || !cb) return;

    if(el->child) {
        ST_free_all_cb(el->child, cb);
    }

    if(el->next) el->next->prev=el->prev;

    if(el->prev) el->prev->next=el->next;
    else {
        if(el->parent) {
            el->parent->child=el->next;
        }
        else {
            if(*tree!=el) printf("ST_remove_cb : wtf : bad tree, i correct it\n");

            *tree=el->next;
        }
     }

    cb(el);
}

inline void ST_remove(sST **tree, sST *el) {
    ST_remove_cb(tree, el, (ST_HANDLER)free);
}

inline void ST_remove_data(sST **tree, sST *el) {
    ST_remove_cb(tree, el, (ST_HANDLER)_ST_free_data_cb);
}

void ST_free_all_cb(sST *tree, ST_HANDLER cb) {    // free tree how the user wants
    if(!cb) return;

    sST *next;

    while(tree) {
        next=tree->next;

        if(tree->child) ST_free_all_cb(tree->child, cb);
        cb(tree);

        tree=next;
    }
}

inline void ST_free_all(sST *tree) {   // free tree
    ST_free_all_cb(tree, (ST_HANDLER)free);
}

inline void ST_free_all_data(sST *tree) {  // free tree and data
    ST_free_all_cb(tree, (ST_HANDLER)_ST_free_data_cb);
}

// ----

void _ST_print(sST *tree, int level);
void _ST_print_debug(sST *tree, unsigned int nd, int level);

inline void ST_print(sST *tree) {
    if(!tree) return;

    _ST_print(tree, 0);
}

inline void ST_print_debug(sST *tree, unsigned int nd) {
    if(!tree) return;

    _ST_print_debug(tree, nd, 0);
}

void _ST_print(sST *tree, int level) {
    char format[16];

    while(tree) {
        sprintf(format, "%%-%ds%%s\n", level);
        printf(format, "", tree->name);
        if(tree->child) _ST_print(tree->child, level+2);
        tree=tree->next;
    }
}

void _ST_print_debug(sST *tree, unsigned int nd, int level) {
    char format[48];
    unsigned int i;

    while(tree) {
        sprintf(format, "[%%08x:%%08x,%%08x,%%08x,%%08x]%%-%ds%%s (", level);
        printf(format, tree, tree->parent, tree->prev, tree->next, tree->child, "", tree->name);

        for(i=0; i<nd; i++)
            printf(i?",%s(%u)":"%s(%u)", FS_NAMES[tree->files[i].status], (unsigned int)tree->files[i].stat.st_mtime);

        printf(")\n");

        if(tree->child) _ST_print_debug(tree->child, nd, level+2);
        tree=tree->next;
    }
}

